#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2024/7/12 13:01
# @File    : model.py
# @Description : -i http://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn




import datetime
import subprocess
import threading
import time
import random

from io import StringIO
import sys,  os

import tkinter as tk
import ttkbootstrap as tkt
from ttkbootstrap import Scrollbar, Text, Button, Checkbutton, Label, Canvas

import logging

logging.basicConfig(
    level=logging.DEBUG,
    format="%(asctime)s | %(levelname)s | %(module)s:%(funcName)s:%(lineno)d - %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
    handlers=[
        logging.FileHandler(filename="./info.log", encoding="utf-8"),
        # logging.StreamHandler(level=logging.DEBUG),
    ],
)

logger = logging.getLogger(__name__)


class WinGUI(tkt.Window):
    def __init__(self):
        super().__init__()
        self.__win()
        self.txt_code = tk.StringVar()
        self.local_env = tk.BooleanVar()
        self.local_env.set(False)
        self.tk_text_code = self.__tk_text_code(self)
        self.tk_button_exe = self.__tk_button_exe(self)
        self.tk_button_clear = self.__tk_button_clear(self)
        self.tk_text_log = self.__tk_text_log(self)

    def __win(self):
        self.title("Test")
        # 设置窗口大小、居中
        width = 400
        height = 300
        screenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        self.geometry(geometry)

        self.resizable(width=False, height=False)

    def scrollbar_autohide(self, vbar, hbar, widget):
        """自动隐藏滚动条"""

        def show():
            if vbar: vbar.lift(widget)
            if hbar: hbar.lift(widget)

        def hide():
            if vbar: vbar.lower(widget)
            if hbar: hbar.lower(widget)

        hide()
        widget.bind("<Enter>", lambda e: show())
        if vbar: vbar.bind("<Enter>", lambda e: show())
        if vbar: vbar.bind("<Leave>", lambda e: hide())
        if hbar: hbar.bind("<Enter>", lambda e: show())
        if hbar: hbar.bind("<Leave>", lambda e: hide())
        widget.bind("<Leave>", lambda e: hide())

    def v_scrollbar(self, vbar, widget, x, y, w, h, pw, ph):
        widget.configure(yscrollcommand=vbar.set)
        vbar.config(command=widget.yview)
        vbar.place(relx=(w + x) / pw, rely=y / ph, relheight=h / ph, anchor='ne')

    def h_scrollbar(self, hbar, widget, x, y, w, h, pw, ph):
        widget.configure(xscrollcommand=hbar.set)
        hbar.config(command=widget.xview)
        hbar.place(relx=x / pw, rely=(y + h) / ph, relwidth=w / pw, anchor='sw')

    def create_bar(self, master, widget, is_vbar, is_hbar, x, y, w, h, pw, ph):
        vbar, hbar = None, None
        if is_vbar:
            vbar = Scrollbar(master)
            self.v_scrollbar(vbar, widget, x, y, w, h, pw, ph)
        if is_hbar:
            hbar = Scrollbar(master, orient="horizontal")
            self.h_scrollbar(hbar, widget, x, y, w, h, pw, ph)
        self.scrollbar_autohide(vbar, hbar, widget)

    def __tk_text_code(self, parent):
        text = Text(parent, )
        text.place(x=5, y=5, width=390, height=150)
        return text

    def __tk_env_check_button(self, parent):
        #cb = Checkbutton(parent, text='default', bootstyle=bootstyle)
        cb = Checkbutton(parent, text='local env', variable=self.local_env)
        cb.configure(compound=tkt.LEFT)
        cb.place(x=200, y=170)
        return cb

    def __tk_button_exe(self, parent):

        self.__tk_env_check_button(parent)

        btn = Button(parent, text="执行", takefocus=False, command=self.exe_code)
        btn.place(x=280, y=160, width=50, height=30)
        return btn

    def __tk_button_clear(self, parent):
        btn = Button(parent, text="清空", takefocus=False, command=self.clear_code)
        btn.place(x=340, y=160, width=50, height=30)
        return btn

    def __tk_text_log(self, parent):
        # text = Text(parent, background='black', foreground="blue")
        # text = Text(parent, font="helvetica 8 bold")
        text = Text(parent)
        text.configure(background="black")
        text.configure(foreground="green")
        # text.configure(spacing2=110)
        # 设置行间距 ， 但是再怎么设置也没有 原生的tkinter间距小（破案了输出多了一个\N）
        text.configure(font=("Sans Serif", 10), spacing1=1, spacing2=5, spacing3=1)
        text.place(x=5, y=195, width=390, height=100)
        # text.insert('end', f"等待执行...")
        return text

    def clear_code(self):
        self.tk_text_code.delete("1.0", "end")

    def log(self, txt):
        update_thread = threading.Thread(target=self.update_log, args=(txt))
        update_thread.start()

    def update_log(self, txt):
        """更新日志的线程函数"""
        logging.info(txt)
        self.tk_text_log.config(state='normal')  # 设置组件为可编辑状态
        if not txt.endswith("\n"):
            txt += "\n"
        self.tk_text_log.insert('end', f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} {txt}")  # 插入最新10条日志
        self.tk_text_log.yview('end')  # 滚动到Text组件底部
        self.tk_text_log.config(state='disabled')  # 设置组件为不可编辑状态，防止用户修改

    def exe_code(self):

        code = self.tk_text_code.get('1.0', 'end-1c')
        flag = self.local_env.get()
        if flag:
            # 保存到文件调用进程使用本地环境执行
            file_name = str(time.time()).replace(".", "")+".py"
            with open(file_name, 'w', encoding="utf-8") as f:
                f.write(code)
            result = subprocess.run(['python', file_name], stdout=subprocess.PIPE, text=True, stderr=subprocess.PIPE, encoding="utf-8")
            os.remove(file_name)
            # 获取输出信息
            if result.stdout is not None:
                # 打印输出信息
                self.update_log(result.stdout)

            if result.returncode == 1 and result.stderr is not None:
                # 记录异常
                self.update_log(result.stderr)
        else:
            # 直接用 exec 执行，
            output = StringIO()
            try:
                # 将打印信息放到 sys 里用于记录输出信息
                sys.stdout = output
                # globals不为空时会默认使用当前系统的所有对象及变量, locals 专递和接收执行代码的变量
                global_vars, local_vars = {}, {}
                exec(code, global_vars, local_vars)
                # 如果代码中定义了一些变量或类 或导入的包 可以通过这个字典拿到
                self.update_log(local_vars.__str__())
            except Exception as e:
                self.update_log(str(e))
            finally:
                log = output.getvalue()
                self.update_log(log)


def init_library(seconds=3):
    # 延迟执行
    time.sleep(seconds)
    print("----------init library----------------")
    #ModuleNotFoundError

    try:
        from prophet import Prophet
        print("Prophet ok.")
    except ModuleNotFoundError as e:
        print("Prophet error.")
    try:
        import tksheet
        import requests
        import ttkbootstrap
        print("tksheet&ttkbootstrap&requests ok.")
    except ModuleNotFoundError as e:
        print("tksheet&ttkbootstrap&requests error.")
    try:
        import matplotlib.pyplot as plt
        from pandas import DataFrame
        print("numpy&pandas&matplotlib ok.")
    except ModuleNotFoundError as e:
        print("numpy&pandas&matplotlib error.")
    print("----------init ok----------------")


if __name__ == "__main__":

    win = WinGUI()

    # 延迟3s 加载所需的包
    task = threading.Thread(target=init_library, args=(3,))
    # 设置线程为守护线程，防止退出主线程时，子线程仍在运行
    task.setDaemon(True)
    task.start()

    win.mainloop()